<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
    

    

    



    <meta charset="utf-8">
    
    
    <meta name="sogou_site_verification" content="true">
    
    
    
    <title>浅谈分布式消息技术 Kafka | Lvshen&#39;s Blog | This is My World</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="分布式,kafka">
    <meta name="baidu-site-verification" content="VIVNdSiMZm">
    <meta name="description" content="This is kafka                 本文转载自 浅谈分布式消息技术 Kafka  Kafka的基本介绍Kafka是最初由Linkedin公司开发，是一个分布式、分区的、多副本的、多订阅者，基">
<meta name="keywords" content="分布式,kafka">
<meta property="og:type" content="article">
<meta property="og:title" content="浅谈分布式消息技术 Kafka">
<meta property="og:url" content="https://lvshen9.gitee.io/2019/01/10/1/index.html">
<meta property="og:site_name" content="Lvshen&#39;s Blog">
<meta property="og:description" content="This is kafka                 本文转载自 浅谈分布式消息技术 Kafka  Kafka的基本介绍Kafka是最初由Linkedin公司开发，是一个分布式、分区的、多副本的、多订阅者，基">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_6f8d46d2-665d-4e28-80eb-2cf1e3fbc7e2.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_3573ea93-7e93-49b4-9779-6765b4fb0878.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_ab744f92-5ee7-4856-aa95-dea5978bc622.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_db460b0e-81c1-432d-94c5-6113e24deb06.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_5c910b0a-7769-4b61-8ef7-f203c6cc6ebf.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_7a05abe1-0f8e-42e3-8967-d1b2c08df29d.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_ea153317-4c86-4880-bbf5-f6e3cc674da8.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_977192fb-57c2-4b1f-a224-34a5225ec343.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_e2fe9558-b7c4-4d40-bd0d-d58d594b01a2.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_040045e5-02e6-4086-819c-146dc37f2db2.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_dfba985b-057b-4d5f-adbb-b2590a5af87d.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_969b4966-9981-47c9-a743-4e4b882339bb.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_4e1d6df1-c2c0-432c-8463-dc43f18444eb.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_de0c3ea1-f84f-4ef9-b223-0ef531165b81.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_94a51a2c-afa2-47e4-975d-1857bd769487.jpg">
<meta property="og:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_e89b49e9-369c-4deb-b9b0-f58f02b33430.jpg">
<meta property="og:updated_time" content="2019-01-17T07:16:51.169Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="浅谈分布式消息技术 Kafka">
<meta name="twitter:description" content="This is kafka                 本文转载自 浅谈分布式消息技术 Kafka  Kafka的基本介绍Kafka是最初由Linkedin公司开发，是一个分布式、分区的、多副本的、多订阅者，基">
<meta name="twitter:image" content="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_6f8d46d2-665d-4e28-80eb-2cf1e3fbc7e2.jpg">
    
    <link rel="shortcut icon" href="/img/mylogo.jpg">
    <link rel="stylesheet" href="//unpkg.com/hexo-theme-material-indigo@latest/css/style.css">
    <script>window.lazyScripts=[]</script>

    <!-- custom head -->
    

</head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap" style="background-image:url(/img/brand.jpg)">
      <div class="brand">
        <a href="/" class="avatar waves-effect waves-circle waves-light">
          <img src="/img/avatar.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">我的技术小房间</h5>
          <a href="mailto:https://lvshen9.github.io" title="https://lvshen9.github.io" class="mail">https://lvshen9.github.io</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                Archives
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                Tags
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/categories"  >
                <i class="icon icon-lg icon-th-list"></i>
                Categories
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/about"  >
                <i class="icon icon-lg icon-address-book"></i>
                About
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/collection"  >
                <i class="icon icon-lg icon-apple"></i>
                Collection
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://lvshen9.github.io/" target="_blank" >
                <i class="icon icon-lg icon-wordpress"></i>
                Blog
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://github.com/lvshen9" target="_blank" >
                <i class="icon icon-lg icon-github-alt"></i>
                GitHub
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">浅谈分布式消息技术 Kafka</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">浅谈分布式消息技术 Kafka</h1>
        <h5 class="subtitle">
            
                <time datetime="2019-01-10T11:33:45.000Z" itemprop="datePublished" class="page-time">
  2019-01-10
</time>


	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/categories/技术/">技术</a></li></ul>

            
        </h5>
    </div>

    


</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap post-toc-shrink" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#Kafka的基本介绍"><span class="post-toc-number">1.</span> <span class="post-toc-text">Kafka的基本介绍</span></a></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#Kafka的设计原理分析"><span class="post-toc-number">2.</span> <span class="post-toc-text">Kafka的设计原理分析</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-5"><a class="post-toc-link" href="#副本（replication）策略"><span class="post-toc-number">2.1.</span> <span class="post-toc-text">副本（replication）策略</span></a></li></ol></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#Kafka消息分组，消息消费原理"><span class="post-toc-number">3.</span> <span class="post-toc-text">Kafka消息分组，消息消费原理</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-5"><a class="post-toc-link" href="#Push-vs-Pull"><span class="post-toc-number">3.1.</span> <span class="post-toc-text">Push vs. Pull</span></a></li></ol></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#Kafak顺序写入与数据读取"><span class="post-toc-number">4.</span> <span class="post-toc-text">Kafak顺序写入与数据读取</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-5"><a class="post-toc-link" href="#顺序写入"><span class="post-toc-number">4.1.</span> <span class="post-toc-text">顺序写入</span></a></li><li class="post-toc-item post-toc-level-5"><a class="post-toc-link" href="#消费者（读取数据）"><span class="post-toc-number">4.2.</span> <span class="post-toc-text">消费者（读取数据）</span></a></li></ol></li></ol>
        </nav>
    </aside>


<article id="post-1"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">浅谈分布式消息技术 Kafka</h1>
        <div class="post-meta">
            <time class="post-time" title="2019-01-10 19:33:45" datetime="2019-01-10T11:33:45.000Z"  itemprop="datePublished">2019-01-10</time>

            
	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/categories/技术/">技术</a></li></ul>



            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_6f8d46d2-665d-4e28-80eb-2cf1e3fbc7e2.jpg" alt="This is kafka" title>
                </div>
                <div class="image-caption">This is kafka</div>
            </figure>  
<blockquote>
<p>本文转载自 <a href="linkedkeeper.com">浅谈分布式消息技术 Kafka</a></p>
</blockquote>
<h4 id="Kafka的基本介绍"><a href="#Kafka的基本介绍" class="headerlink" title="Kafka的基本介绍"></a>Kafka的基本介绍</h4><p>Kafka是最初由Linkedin公司开发，是一个分布式、分区的、多副本的、多订阅者，基于zookeeper协调的分布式日志系统（也可以当做MQ系统），常见可以用于web/nginx日志、访问日志，消息服务等等，Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。主要应用场景是：日志收集系统和消息系统。 Kafka主要设计目标如下：以时间复杂度为O(1)的方式提供消息持久化能力，即使对TB级以上数据也能保证常数时间的访问性能。 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输。支持Kafka Server间的消息分区，及分布式消费，同时保证每个partition内的消息顺序传输。同时支持离线数据处理和实时数据处理。 </p>
<a id="more"></a>
<h4 id="Kafka的设计原理分析"><a href="#Kafka的设计原理分析" class="headerlink" title="Kafka的设计原理分析"></a>Kafka的设计原理分析</h4><figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_3573ea93-7e93-49b4-9779-6765b4fb0878.jpg" alt="kafka总体结构" title>
                </div>
                <div class="image-caption">kafka总体结构</div>
            </figure>一个典型的kafka集群中包含若干producer，若干broker，若干consumer，以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置，选举leader，以及在consumer group发生变化时进行rebalance。producer使用push模式将消息发布到broker，consumer使用pull模式从broker订阅并消费消息。 　Kafka专用术语：<br><br>- Broker：消息中间件处理结点，一个Kafka节点就是一个broker，多个broker可以组成一个Kafka集群。<br>- Topic：一类消息，Kafka集群能够同时负责多个topic的分发。<br>- Partition：topic物理上的分组，一个topic可以分为多个partition，每个partition是一个有序的队列。<br>- Segment：partition物理上由多个segment组成。<br>- offset：每个partition都由一系列有序的、不可变的消息组成，这些消息被连续的追加到partition中。partition中的每个消息都有一个连续的序列号叫做offset，用于partition唯一标识一条消息。<br>- Producer：负责发布消息到Kafka broker。<br>- Consumer：消息消费者，向Kafka broker读取消息的客户端。<br>- Consumer Group：每个Consumer属于一个特定的Consumer Group。<br><br>#### Kafka数据传输的事务特点<br><br>- at most once：最多一次，这个和JMS中”非持久化”消息类似，发送一次，无论成败，将不会重发。消费者fetch消息，然后保存offset，然后处理消息；当client保存offset之后，但是在消息处理过程中出现了异常，导致部分消息未能继续处理。那么此后”未处理”的消息将不能被fetch到，这就是”at most once”。<br>- at least once：消息至少发送一次，如果消息未能接受成功，可能会重发，直到接收成功。消费者fetch消息，然后处理消息，然后保存offset。如果消息处理成功之后，但是在保存offset阶段zookeeper异常导致保存操作未能执行成功，这就导致接下来再次fetch时可能获得上次已经处理过的消息，这就是”at least once”，原因offset没有及时的提交给zookeeper，zookeeper恢复正常还是之前offset状态。<br>- exactly once：消息只会发送一次。kafka中并没有严格的去实现（基于2阶段提交），我们认为这种策略在kafka中是没有必要的。 通常情况下”at-least-once”是我们首选。<br><br>#### Kafka消息存储格式<br><br>##### Topic &amp; Partition<br><br>一个topic可以认为一个一类消息，每个topic将被分成多个partition，每个partition在存储层面是append log文件。<br><br><figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_ab744f92-5ee7-4856-aa95-dea5978bc622.jpg" alt="kafka的topic与partition" title>
                </div>
                <div class="image-caption">kafka的topic与partition</div>
            </figure>
<p>在Kafka文件存储中，同一个topic下有多个不同partition，每个partition为一个目录，partiton命名规则为topic名称+有序序号，第一个partiton序号从0开始，序号最大值为partitions数量减1。</p>
<p> <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_db460b0e-81c1-432d-94c5-6113e24deb06.jpg" alt="kafka的partition"></p>
<ul>
<li>每个partion（目录）相当于一个巨型文件被平均分配到多个大小相等segment（段）数据文件中。但每个段segment file消息数量不一定相等，这种特性方便old segment file快速被删除。</li>
<li>每个partiton只需要支持顺序读写就行了，segment文件生命周期由服务端配置参数决定。</li>
</ul>
<p>这样做的好处就是能快速删除无用文件，有效提高磁盘利用率。</p>
<ul>
<li>segment file组成：由2大部分组成，分别为index file和data file，此2个文件一一对应，成对出现，后缀”.index”和“.log”分别表示为segment索引文件、数据文件.</li>
<li>segment文件命名规则：partion全局的第一个segment从0开始，后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小，19位数字字符长度，没有数字用0填充。</li>
</ul>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_5c910b0a-7769-4b61-8ef7-f203c6cc6ebf.jpg" alt="存储文件" title>
                </div>
                <div class="image-caption">存储文件</div>
            </figure>
<p>segment中index与data file对应关系物理结构如下：</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_7a05abe1-0f8e-42e3-8967-d1b2c08df29d.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure>
<p>上图中索引文件存储大量元数据，数据文件存储大量消息，索引文件中元数据指向对应数据文件中message的物理偏移地址。其中以索引文件中元数据3,497为例，依次在数据文件中表示第3个message（在全局partiton表示第368772个message），以及该消息的物理偏移地址为497。</p>
<p>了解到segment data file由许多message组成，下面详细说明message物理结构如下：</p>
<p> <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_ea153317-4c86-4880-bbf5-f6e3cc674da8.jpg" alt="massage的物理结构"></p>
<p>参数说明：</p>
<table>
<thead>
<tr>
<th>关键字</th>
<th>解释说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>8 byte offset</td>
<td>在parition(分区)内的每条消息都有一个有序的id号，这个id号被称为偏移(offset),它可以唯一确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message</td>
</tr>
<tr>
<td>4 byte message size</td>
<td>message大小</td>
</tr>
<tr>
<td>4 byte CRC32</td>
<td>用crc32校验message</td>
</tr>
<tr>
<td>1 byte “magic”</td>
<td>表示本次发布Kafka服务程序协议版本号</td>
</tr>
<tr>
<td>1 byte “attributes”</td>
<td>表示为独立版本、或标识压缩类型、或编码类型。</td>
</tr>
<tr>
<td>4 byte key length</td>
<td>表示key的长度,当key为-1时，K byte key字段不填</td>
</tr>
<tr>
<td>K byte key</td>
<td>可选</td>
</tr>
<tr>
<td>value bytes payload</td>
<td>表示实际消息数据。</td>
</tr>
</tbody>
</table>
<h5 id="副本（replication）策略"><a href="#副本（replication）策略" class="headerlink" title="副本（replication）策略"></a>副本（replication）策略</h5><p>Kafka的高可靠性的保障来源于其健壮的副本（replication）策略。</p>
<p>1) 数据同步</p>
<p>kafka在0.8版本前没有提供Partition的Replication机制，一旦Broker宕机，其上的所有Partition就都无法提供服务，而Partition又没有备份数据，数据的可用性就大大降低了。所以0.8后提供了Replication机制来保证Broker的failover。引入Replication之后，同一个Partition可能会有多个Replica，而这时需要在这些Replication之间选出一个Leader，Producer和Consumer只与这个Leader交互，其它Replica作为Follower从Leader中复制数据。</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_977192fb-57c2-4b1f-a224-34a5225ec343.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure> 
<p>2) 副本放置策略 </p>
<p>为了更好的做负载均衡，Kafka尽量将所有的Partition均匀分配到整个集群上。Kafka分配Replica的算法如下：</p>
<ul>
<li>将所有存活的N个Brokers和待分配的Partition排序</li>
<li>将第i个Partition分配到第(i mod n)个Broker上，这个Partition的第一个Replica存在于这个分配的Broker上，并且会作为partition的优先副本</li>
<li>将第i个Partition的第j个Replica分配到第((i + j) mod n)个Broker上假设集群一共有4个brokers，一个topic有4个partition，每个Partition有3个副本。下图是每个Broker上的副本分配情况。</li>
</ul>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_e2fe9558-b7c4-4d40-bd0d-d58d594b01a2.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure>
<p>3) 同步策略</p>
<p>Producer在发布消息到某个Partition时，先通过ZooKeeper找到该Partition的Leader，然后无论该Topic的Replication Factor为多少，Producer只将该消息发送到该Partition的Leader。Leader会将该消息写入其本地Log。每个Follower都从Leader pull数据。这种方式上，Follower存储的数据顺序与Leader保持一致。Follower在收到该消息并写入其Log后，向Leader发送ACK。</p>
<p>一旦Leader收到了ISR中的所有Replica的ACK，该消息就被认为已经commit了，Leader将增加HW并且向Producer发送ACK。为了提高性能，每个Follower在接收到数据后就立马向Leader发送ACK，而非等到数据写入Log中。因此，对于已经commit的消息，Kafka只能保证它被存于多个Replica的内存中，而不能保证它们被持久化到磁盘中，也就不能完全保证异常发生后该条消息一定能被Consumer消费。</p>
<p>Consumer读消息也是从Leader读取，只有被commit过的消息才会暴露给Consumer。Kafka Replication的数据流如下图所示： </p>
<p><img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_040045e5-02e6-4086-819c-146dc37f2db2.jpg" alt>对于Kafka而言，定义一个Broker是否“活着”包含两个条件：</p>
<ul>
<li>一是它必须维护与ZooKeeper的session（这个通过ZooKeeper的Heartbeat机制来实现）。</li>
<li>二是Follower必须能够及时将Leader的消息复制过来，不能“落后太多”。</li>
</ul>
<p>Leader会跟踪与其保持同步的Replica列表，该列表称为ISR（即in-sync Replica）。如果一个Follower宕机，或者落后太多，Leader将把它从ISR中移除。这里所描述的“落后太多”指Follower复制的消息落后于Leader后的条数超过预定值或者Follower超过一定时间未向Leader发送fetch请求。</p>
<p>Kafka只解决fail/recover，一条消息只有被ISR里的所有Follower都从Leader复制过去才会被认为已提交。这样就避免了部分数据被写进了Leader，还没来得及被任何Follower复制就宕机了，而造成数据丢失（Consumer无法消费这些数据）。而对于Producer而言，它可以选择是否等待消息commit。这种机制确保了只要ISR有一个或以上的Follower，一条被commit的消息就不会丢失。</p>
<p>4) leader选举</p>
<p>Leader选举本质上是一个分布式锁，有两种方式实现基于ZooKeeper的分布式锁：</p>
<ul>
<li>节点名称唯一性：多个客户端创建一个节点，只有成功创建节点的客户端才能获得锁</li>
<li>临时顺序节点：所有客户端在某个目录下创建自己的临时顺序节点，只有序号最小的才获得锁</li>
</ul>
<p>Majority Vote的选举策略和ZooKeeper中的Zab选举是类似的，实际上ZooKeeper内部本身就实现了少数服从多数的选举策略。kafka中对于Partition的leader副本的选举采用了第一种方法：为Partition分配副本，指定一个ZNode临时节点，第一个成功创建节点的副本就是Leader节点，其他副本会在这个ZNode节点上注册Watcher监听器，一旦Leader宕机，对应的临时节点就会被自动删除，这时注册在该节点上的所有Follower都会收到监听器事件，它们都会尝试创建该节点，只有创建成功的那个follower才会成为Leader（ZooKeeper保证对于一个节点只有一个客户端能创建成功），其他follower继续重新注册监听事件。</p>
<h4 id="Kafka消息分组，消息消费原理"><a href="#Kafka消息分组，消息消费原理" class="headerlink" title="Kafka消息分组，消息消费原理"></a>Kafka消息分组，消息消费原理</h4><p>同一Topic的一条消息只能被同一个Consumer Group内的一个Consumer消费，但多个Consumer Group可同时消费这一消息。</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_dfba985b-057b-4d5f-adbb-b2590a5af87d.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure>
<p>这是Kafka用来实现一个Topic消息的广播（发给所有的Consumer）和单播（发给某一个Consumer）的手段。一个Topic可以对应多个Consumer Group。如果需要实现广播，只要每个Consumer有一个独立的Group就可以了。要实现单播只要所有的Consumer在同一个Group里。用Consumer Group还可以将Consumer进行自由的分组而不需要多次发送消息到不同的Topic。</p>
<h5 id="Push-vs-Pull"><a href="#Push-vs-Pull" class="headerlink" title="Push vs. Pull"></a>Push vs. Pull</h5><p>作为一个消息系统，Kafka遵循了传统的方式，选择由Producer向broker push消息并由Consumer从broker pull消息。</p>
<p>push模式很难适应消费速率不同的消费者，因为消息发送速率是由broker决定的。push模式的目标是尽可能以最快速度传递消息，但是这样很容易造成Consumer来不及处理消息，典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据Consumer的消费能力以适当的速率消费消息。</p>
<p>对于Kafka而言，pull模式更合适。pull模式可简化broker的设计，Consumer可自主控制消费消息的速率，同时Consumer可以自己控制消费方式——即可批量消费也可逐条消费，同时还能选择不同的提交方式从而实现不同的传输语义。</p>
<h4 id="Kafak顺序写入与数据读取"><a href="#Kafak顺序写入与数据读取" class="headerlink" title="Kafak顺序写入与数据读取"></a>Kafak顺序写入与数据读取</h4><p>生产者（producer）是负责向Kafka提交数据的，Kafka会把收到的消息都写入到硬盘中，它绝对不会丢失数据。为了优化写入速度Kafak采用了两个技术，顺序写入和MMFile。</p>
<h5 id="顺序写入"><a href="#顺序写入" class="headerlink" title="顺序写入"></a>顺序写入</h5><p>因为硬盘是机械结构，每次读写都会寻址，写入，其中寻址是一个“机械动作”，它是最耗时的。所以硬盘最“讨厌”随机I/O，最喜欢顺序I/O。为了提高读写硬盘的速度，Kafka就是使用顺序I/O。</p>
<p>每条消息都被append到该Partition中，属于顺序写磁盘，因此效率非常高。</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_969b4966-9981-47c9-a743-4e4b882339bb.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure>
<p>对于传统的message queue而言，一般会删除已经被消费的消息，而Kafka是不会删除数据的，它会把所有的数据都保留下来，每个消费者（Consumer）对每个Topic都有一个offset用来表示读取到了第几条数据。</p>
<p><img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_4e1d6df1-c2c0-432c-8463-dc43f18444eb.jpg" alt></p>
<p>即便是顺序写入硬盘，硬盘的访问速度还是不可能追上内存。所以Kafka的数据并不是实时的写入硬盘，它充分利用了现代操作系统分页存储来利用内存提高I/O效率。</p>
<p>在Linux Kernal 2.2之后出现了一种叫做“零拷贝(zero-copy)”系统调用机制，就是跳过“用户缓冲区”的拷贝，建立一个磁盘空间和内存空间的直接映射，数据不再复制到“用户态缓冲区”系统上下文切换减少2次，可以提升一倍性能。</p>
<p><img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_de0c3ea1-f84f-4ef9-b223-0ef531165b81.jpg" alt></p>
<p>通过mmap，进程像读写硬盘一样读写内存（当然是虚拟机内存）。使用这种方式可以获取很大的I/O提升，省去了用户空间到内核空间复制的开销（调用文件的read会把数据先放到内核空间的内存中，然后再复制到用户空间的内存中。）</p>
<h5 id="消费者（读取数据）"><a href="#消费者（读取数据）" class="headerlink" title="消费者（读取数据）"></a>消费者（读取数据）</h5><p>试想一下，一个Web Server传送一个静态文件，如何优化？答案是zero copy。传统模式下我们从硬盘读取一个文件是这样的。</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_94a51a2c-afa2-47e4-975d-1857bd769487.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure>
<p>先复制到内核空间（read是系统调用，放到了DMA，所以用内核空间），然后复制到用户空间（1、2）；从用户空间重新复制到内核空间（你用的socket是系统调用，所以它也有自己的内核空间），最后发送给网卡（3、4）。</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://misc.linkedkeeper.com/misc/img/blog/201707/linkedkeeper0_e89b49e9-369c-4deb-b9b0-f58f02b33430.jpg" alt title>
                </div>
                <div class="image-caption"></div>
            </figure>
<p>Zero Copy中直接从内核空间（DMA的）到内核空间（Socket的），然后发送网卡。这个技术非常普遍，Nginx也是用的这种技术。</p>
<p>实际上，Kafka把所有的消息都存放在一个一个的文件中，当消费者需要数据的时候Kafka直接把“文件”发送给消费者。当不需要把整个文件发出去的时候，Kafka通过调用Zero Copy的sendfile这个函数，这个函数包括：</p>
<ul>
<li>out_fd作为输出（一般及时socket的句柄）</li>
<li>in_fd作为输入文件句柄</li>
<li>off_t表示in_fd的偏移（从哪里开始读取）</li>
<li>size_t表示读取多少个</li>
</ul>
<p>「 浅谈大规模分布式系统中那些技术点」系列文章：</p>
<ul>
<li><a href="http://www.linkedkeeper.com/detail/blog.action?bid=1013" target="_blank" rel="noopener">浅谈分布式事务</a></li>
<li><a href="http://www.linkedkeeper.com/detail/blog.action?bid=1014" target="_blank" rel="noopener">浅谈分布式服务协调技术 Zookeeper</a></li>
</ul>
<p><strong>Reference</strong></p>
<p><a href="http://www.cnblogs.com/liuming1992/p/6423007.html" target="_blank" rel="noopener">http://www.cnblogs.com/liuming1992/p/6423007.html</a></p>
<p><a href="http://blog.csdn.net/lifuxiangcaohui/article/details/51374862" target="_blank" rel="noopener">http://blog.csdn.net/lifuxiangcaohui/article/details/51374862</a></p>
<p><a href="http://www.jasongj.com/2015/01/02/Kafka深度解析" target="_blank" rel="noopener">http://www.jasongj.com/2015/01/02/Kafka深度解析</a></p>
<p><a href="http://www.infoq.com/cn/articles/kafka-analysis-part-2" target="_blank" rel="noopener">http://www.infoq.com/cn/articles/kafka-analysis-part-2</a></p>
<p><a href="http://zqhxuyuan.github.io/2016/02/23/2016-02-23-Kafka-Controller" target="_blank" rel="noopener">http://zqhxuyuan.github.io/2016/02/23/2016-02-23-Kafka-Controller</a></p>
<p><a href="https://tech.meituan.com/kafka-fs-design-theory.html" target="_blank" rel="noopener">https://tech.meituan.com/kafka-fs-design-theory.html</a></p>
<p><a href="https://my.oschina.net/silence88/blog/856195" target="_blank" rel="noopener">https://my.oschina.net/silence88/blog/856195</a></p>
<p><a href="https://toutiao.io/posts/508935/app_preview" target="_blank" rel="noopener">https://toutiao.io/posts/508935/app_preview</a></p>

        </div>

        <blockquote class="post-copyright">
    
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2019-01-17T07:16:51.169Z" itemprop="dateUpdated">2019-01-17 15:16:51</time>
</span><br>


        
        原文链接：<a href="/2019/01/10/1/" target="_blank" rel="external">https://lvshen9.gitee.io/2019/01/10/1/</a>
        
    </div>
    
    <footer>
        <a href="https://lvshen9.gitee.io">
            <img src="/img/avatar.jpg" alt="我的技术小房间">
            我的技术小房间
        </a>
    </footer>
</blockquote>

        
<div class="page-reward">
    <a id="rewardBtn" href="javascript:;" class="page-reward-btn waves-effect waves-circle waves-light">赏</a>
</div>



        <div class="post-footer">
            
	<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/kafka/">kafka</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/分布式/">分布式</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=https://lvshen9.gitee.io/2019/01/10/1/&title=《浅谈分布式消息技术 Kafka》 — Lvshen's Blog&pic=https://lvshen9.gitee.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://lvshen9.gitee.io/2019/01/10/1/&title=《浅谈分布式消息技术 Kafka》 — Lvshen's Blog&source=
                
                    
                    
                
..." data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://lvshen9.gitee.io/2019/01/10/1/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《浅谈分布式消息技术 Kafka》 — Lvshen's Blog&url=https://lvshen9.gitee.io/2019/01/10/1/&via=https://lvshen9.gitee.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=https://lvshen9.gitee.io/2019/01/10/1/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between">
  
    <div class="waves-block waves-effect prev">
      <a href="/2019/01/11/1/" id="post-prev" class="post-nav-link">
        <div class="tips"><i class="icon icon-angle-left icon-lg icon-pr"></i> Prev</div>
        <h4 class="title">我在github上面的一个项目————用Python爬取12306火车票</h4>
      </a>
    </div>
  

  
    <div class="waves-block waves-effect next">
      <a href="/2018/11/28/Log4X统一日志平台部署记录/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">Log4X统一日志平台部署记录</h4>
      </a>
    </div>
  
</nav>



    











    <!-- Valine Comments -->
    <div class="comments vcomment" id="comments"></div>
    <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
    <script src="//unpkg.com/valine@latest/dist/Valine.min.js"></script>
    <!-- Valine Comments script -->
    <script>
        var GUEST_INFO = ['nick','mail','link'];
        var guest_info = 'nick,mail,link'.split(',').filter(function(item){
          return GUEST_INFO.indexOf(item) > -1
        });
        new Valine({
            el: '#comments',
            notify: 'false' == 'true',
            verify: 'false' == 'true',
            appId: "dy9kXHwg5jQUlLryQmpjWRlM-gzGzoHsz",
            appKey: "P9Nh39Ol0JbMMiYqNGHEP3ml",
            avatar: "mm",
            placeholder: "Just go go",
            guest_info: guest_info.length == 0 ? GUEST_INFO : guest_info,
            pageSize: "10"
        })
    </script>
    <!-- Valine Comments end -->







</article>

<div id="reward" class="page-modal reward-lay">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <h3 class="reward-title">
        <i class="icon icon-quote-left"></i>
        谢谢大爷~
        <i class="icon icon-quote-right"></i>
    </h3>
    <div class="reward-content">
        
        <div class="reward-code">
            <img id="rewardCode" src="https://lvshen9.github.io/blog2/pay/weixin.jpg" alt="打赏二维码">
        </div>
        
        <label class="reward-toggle">
            <input id="rewardToggle" type="checkbox" class="reward-toggle-check"
                data-wechat="https://lvshen9.github.io/blog2/pay/weixin.jpg" data-alipay="https://lvshen9.github.io/blog2/pay/zhifu.jpg">
            <div class="reward-toggle-ctrol">
                <span class="reward-toggle-item wechat">微信</span>
                <span class="reward-toggle-label"></span>
                <span class="reward-toggle-item alipay">支付宝</span>
            </div>
        </label>
        
    </div>
</div>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            
            <span>博客内容遵循 <a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">知识共享 署名 - 非商业性 - 相同方式共享 4.0 国际协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p><span>我的技术小房间 &copy; 2015 - 2020</span>
            <span>
                
                Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="https://github.com/yscoder/hexo-theme-indigo" target="_blank">indigo</a>
            </span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=https://lvshen9.gitee.io/2019/01/10/1/&title=《浅谈分布式消息技术 Kafka》 — Lvshen's Blog&pic=https://lvshen9.gitee.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://lvshen9.gitee.io/2019/01/10/1/&title=《浅谈分布式消息技术 Kafka》 — Lvshen's Blog&source=
                
                    
                    
                
..." data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://lvshen9.gitee.io/2019/01/10/1/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《浅谈分布式消息技术 Kafka》 — Lvshen's Blog&url=https://lvshen9.gitee.io/2019/01/10/1/&via=https://lvshen9.gitee.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=https://lvshen9.gitee.io/2019/01/10/1/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="//api.qrserver.com/v1/create-qr-code/?data=https://lvshen9.gitee.io/2019/01/10/1/" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/', SHARE: true, REWARD: true };


</script>

<script src="//unpkg.com/hexo-theme-material-indigo@latest/js/main.min.js"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="//unpkg.com/hexo-theme-material-indigo@latest/js/search.min.js" async></script>






<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>



<script>
(function() {
    var OriginTitile = document.title, titleTime;
    document.addEventListener('visibilitychange', function() {
        if (document.hidden) {
            document.title = '死鬼去哪里了！';
            clearTimeout(titleTime);
        } else {
            document.title = '(つェ⊂)咦!又好了!';
            titleTime = setTimeout(function() {
                document.title = OriginTitile;
            },2000);
        }
    });
})();
</script>



</body>
</html>
